package com.jmxcfc.blfsc.xuanwu.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.esms.MessageData;
import com.esms.PostMsg;
import com.esms.common.entity.Account;
import com.esms.common.entity.GsmsResponse;
import com.esms.common.entity.MTPack;
import com.jmxcfc.blfsc.common.common.request.BaseRequest;
import com.jmxcfc.blfsc.common.common.response.BaseResponse;
import com.jmxcfc.blfsc.common.exception.*;
import com.jmxcfc.blfsc.common.sms.BaseContext;
import com.jmxcfc.blfsc.common.sms.SmsRequest;
import com.jmxcfc.blfsc.common.common.enums.ProcessStateEnum;
import com.jmxcfc.blfsc.common.sms.extend.SmsExtend;
import com.jmxcfc.blfsc.common.validation.rule.RequestFieldRule;
import com.jmxcfc.blfsc.common.validation.rule.SmsRulesUtil;
import com.jmxcfc.blfsc.xuanwu.common.Constants;
import com.jmxcfc.blfsc.xuanwu.config.XuanwuProperties;
import com.jmxcfc.blfsc.xuanwu.entity.SendDetailSub;
import com.jmxcfc.blfsc.xuanwu.service.ISendDetailSubService;
import com.jmxcfc.blfsc.xuanwu.service.ISmsRequestService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.UUID;

/**
 * <p>
 * 玄武短信信息表 服务实现类
 * </p>
 *
 * @author pbs
 * @since 2023-09-06 2:43:31
 */
@Slf4j
@Service
@RefreshScope
public class SmsRequestServiceImpl implements ISmsRequestService , InitializingBean {

    private XuanwuProperties xuanwuProperties;
    private ISendDetailSubService sendDetailSubService;
    private PostMsg postMsg;
    @Value("${com.xuanwu.soTimeout}")
    private int soTimeout;

    @Value("${com.xuanwu.maxConns}")
    private int maxConns;


    @Autowired
    public void setXuanwuProperties(XuanwuProperties xuanwuProperties) {
        this.xuanwuProperties = xuanwuProperties;
    }
    @Autowired
    public void setSendDetailSubService(ISendDetailSubService sendDetailSubService) {
        this.sendDetailSubService = sendDetailSubService;
    }


    /**
     * 发短信请求
     * 校验:
     * requestNo(内部的请求流水号)不为空、
     * 批次号(批次号)不为空、
     * sendDtl(发送集合)不为空
     * 扩展字段检验:
     * 账号(即业务类型)
     * @param requestMessage
     * @return
     */
    @Override
    public BaseResponse<JSONObject> sendRequest(BaseRequest<SmsRequest<SmsExtend>> requestMessage) {
        BaseContext<GsmsResponse> baseContext = new BaseContext<>();
        //头部参数校验规则:业务号、请求流水号字段(玄武批次号字段)
        List<RequestFieldRule<?>> headerElementRules = baseContext.getHeaderElementRules();
        headerElementRules.add(SmsRulesUtil.getRequestNoRule());
        headerElementRules.add(SmsRulesUtil.getSerialSeqRule("批次号字段(玄武参数batchId)规则检验不符合"));

        //请求体参数校验:发送集合
        List<RequestFieldRule<?>> bodyElementRules = baseContext.getBodyElementRules();
        bodyElementRules.add(SmsRulesUtil.getSendDtlRule());

        //请求体扩展字段校验:玄武的业务类型校验
        List<RequestFieldRule<?>> bodyExtendElementRules = baseContext.getBodyExtendElementRules();
        bodyExtendElementRules.add(SmsRulesUtil.getAcctNolRule("账号(业务类型)字段规则检验不符合"));
        return execute(requestMessage, baseContext);
    }

    /**
     * 扩展字段校验账号字段不为空
     * @param body 请求体(不能传JSONObject)
     *备注:每个实现类需要自己实现该方法,然后进行扩展字段的校验
     * @param baseHandleContext
     * @param <E>
     * @throws ValidBodyException
     */
    @Override
    public <E> void validBodyExtend(SmsRequest<SmsExtend> body, BaseContext<E> baseHandleContext) throws ValidBodyException {
        try {
            if (Objects.nonNull(baseHandleContext)) {
                baseHandleContext.getBodyExtendElementRules().forEach(rule -> valid(rule, body.getExtend()));
            }
        } catch (RuntimeException e) {
            throw new ValidBodyException(e.getMessage());
        }
    }


    /**
     * 参数组装
     * 1、serialSeq=>批次号字段batchId
     * @param request
     * @param response
     * @param baseContext
     * @throws BeforeOutboundException
     */
    @Override
    @Transactional(rollbackFor = RuntimeException.class)
    public void beforeOutbound(BaseRequest<SmsRequest<SmsExtend>> request, BaseResponse<JSONObject> response, BaseContext<GsmsResponse> baseContext) throws BeforeOutboundException {
        String requestNo = request.getHeader().getRequestNo();
        String batchId = request.getHeader().getSerialSeq();
        try {
            log.info("短信请求业务号requestNo:{},批次号bathId:{},前置处理:数据落库和参数组装",requestNo,batchId);
            List<SendDetailSub> sendDetailSubList = new ArrayList<>();
            MTPack mtPack = buildParam(request, sendDetailSubList);
            HttpEntity<MTPack> requestEntity = new HttpEntity<>(mtPack, new HttpHeaders());
            baseContext.setRestHttpEntity(requestEntity);
            //数据保存
            saveBatch(requestNo,sendDetailSubList);
            log.info("短信请求业务号requestNo:{},批次号:{},保存明细表数据条数:{},前置处理结束", requestNo, batchId, sendDetailSubList.size());
        } catch (Exception e) {
            log.error("短信请求业务号requestNo:{},前置处理异常:{}", requestNo, e.getMessage(), e);
            throw new BeforeOutboundException(e.getMessage(), e);
        }
    }


    /**
     * 外呼
     * @param request
     * @param response
     * @param baseContext
     * @throws OutboundException
     */
    @Override
    public void outbound(BaseRequest<SmsRequest<SmsExtend>> request, BaseResponse<JSONObject> response, BaseContext<GsmsResponse> baseContext) throws OutboundException {
        try {
            log.info("短信请求业务号requestNo:{},玄武发送短信请求外呼,请求地址:{},端口:{},请参数:{}",request.getHeader().getRequestNo(),xuanwuProperties.getIp(),xuanwuProperties.getPort(),baseContext.getRestHttpEntity().getBody());
            //todo 玄武这边设置参数需要确认
          /*  PostMsg pm = new PostMsg(false, soTimeout, maxConns);
            //设置下发服务器的地址信息,gwHost设置(请求)
            pm.getCmHost().setHost(xuanwuProperties.getIp(), xuanwuProperties.getPort());*/
            MTPack mtPack = (MTPack) baseContext.getRestHttpEntity().getBody();
            GsmsResponse resp = postMsg.post(new Account(xuanwuProperties.getAccount(), xuanwuProperties.getPassword()), mtPack);

            log.info("短信请求业务号requestNo:{},玄武发送短信请求外呼,响应内容:{}", request.getHeader().getRequestNo(), JSON.toJSONString(resp));
            baseContext.setRestResponse(resp);
        } catch (Exception e) {
            log.error("短信请求业务号requestNo:{},集微发送短信请求外呼异常:{}", request.getHeader().getRequestNo(), e.getMessage());
            throw new OutboundException(e.getMessage(), e);
        }
    }

    /**
     * 状态判断、数据更新
     * @param request
     * @param response
     * @param baseContext
     * @throws AfterOutboundException
     */
    @Override
    @Transactional(rollbackFor = RuntimeException.class)
    public void afterOutbound(BaseRequest<SmsRequest<SmsExtend>> request, BaseResponse<JSONObject> response, BaseContext<GsmsResponse> baseContext) throws AfterOutboundException {
        String requestNo = request.getHeader().getRequestNo();
        try {
            String batchId = request.getHeader().getSerialSeq();
            log.info("短信请求业务号requestNo:{},批次号:{},后置处理:解析响应结果", requestNo, batchId);
            GsmsResponse gsmsResponse = baseContext.getRestResponse();
            log.info("短信请求业务号requestNo:{},玄武响应的result:{},信息描述:{}", requestNo, gsmsResponse.getResult(), gsmsResponse.getMessage());
            String resultStr = String.valueOf(gsmsResponse.getResult());
            //0是接口交行成功,短信终态需要主动拉取
            if (StringUtils.equals(resultStr,"0")) {
                response.getHeader().setProcessState(ProcessStateEnum.PROCESSING);
            }else {
                //终态:失败
                response.getHeader().setProcessState(ProcessStateEnum.FAILURE);
            }
            UpdateWrapper<SendDetailSub> updateWrapper = new UpdateWrapper<>();
            updateWrapper.lambda().set(SendDetailSub::getErrorResult, resultStr)
                    .set(SendDetailSub::getErrorMsg, gsmsResponse.getMessage())
                    .eq(SendDetailSub::getRequestNo, requestNo)
                    .eq(SendDetailSub::getBatchId, batchId);
            sendDetailSubService.update(new SendDetailSub(), updateWrapper);
            log.info("短信请求业务号requestNo:{},根据requestNo、batchId条件更新明细表完成",requestNo);

            response.setBody(JSON.parseObject(JSON.toJSONString(gsmsResponse)));
            response.getHeader().setResCode(resultStr);
            response.getHeader().setResMessage(gsmsResponse.getMessage());
            //response.setExtend(JSON.parseObject(JSON.toJSONString(gsmsResponse)));
            //response.setErrorCode(resultStr);
            //response.setErrorDesc(gsmsResponse.getMessage());
            log.info("短信请求业务号requestNo:{},后置处理结束",requestNo);
        } catch (Exception e) {
            log.error("短信请求业务号requestNo:{},后置处理异常:{}", requestNo, e.getMessage(), e);
            throw new AfterOutboundException(e.getMessage(), e);
        }
    }






    /**
     * 数据保存
     * @param requestNo
     * @param sendDetailSubList
     */
    private void saveBatch(String requestNo,List<SendDetailSub> sendDetailSubList) {
        try {
            sendDetailSubService.saveBatch(sendDetailSubList);
        } catch (DuplicateKeyException e) {
            log.error("请求流水号requestNo:{},保存玄武明细数据,存在重复主键Id,违反唯一约束条件", requestNo);
            throw new RepetitionException(e);
        }
    }

    /**
     * 参数组装
     * @param request
     * @param sendDetailSubList
     * @return
     */
    private MTPack buildParam(BaseRequest<SmsRequest<SmsExtend>> request, List<SendDetailSub> sendDetailSubList)throws Exception {
        //业务号
        String requestNo = request.getHeader().getRequestNo();
        //批次号
        String batchId = request.getHeader().getSerialSeq();
        if (CollectionUtils.isEmpty(request.getBody().getSendDtl())){
            log.info("短信请求业务号requestNo:{},批次号bathId:{},请求参数的发送集合是空", requestNo, batchId);
            throw new BeforeOutboundException("短信发送集合sendDtl字段为空!");
        }
        List<MessageData> messageDataList = new ArrayList<>();
        //组装mtPack
        MTPack mtPack = getPack(request.getBody(),batchId);
        request.getBody().getSendDtl().forEach(messageParamItem -> {
            MessageData messageData = new MessageData(messageParamItem.getCellPhoneNo(), messageParamItem.getContent(), messageParamItem.getUuid());
            messageDataList.add(messageData);
            //数据赋值
            SendDetailSub sendDetailSub = new SendDetailSub();
            sendDetailSub.setUuid(messageParamItem.getUuid());
            sendDetailSub.setCellPhoneNo(messageParamItem.getCellPhoneNo());
            sendDetailSub.setRequestNo(requestNo);
            //mtPack默认是false
            sendDetailSub.setDistinctFlag(String.valueOf(mtPack.isDistinctFlag()));
            sendDetailSub.setBatchName(mtPack.getBatchName());
            //sendDetailSub.setTemplateParam();
            //sendDetailSub.setMsgContent();
            //业务类型(1:默认业务类型、13:催收业务类型、14:营销业务类、16:通知业务类型)
            sendDetailSub.setBizType( request.getBody().getExtend().getAcctNo());
            sendDetailSub.setBatchId(batchId);
            sendDetailSub.setSendType(mtPack.getSendType().name());
            //定时发送时间(格式:yyyyMMddHHmmss),暂时没有用的
            if (StringUtils.isNotBlank( request.getBody().getScheduleTime())) {
                //对方接口文档提供的时间格式是yyyyMMddHHmmss, blfs-sms传过来的格式是:yyyy-MM-dd HH:mm:ss
                String scheduleTime = LocalDateTime.parse( request.getBody().getScheduleTime(), DateTimeFormatter.ofPattern(Constants.TIME_FORMATTER)).format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss"));
                sendDetailSub.setScheduleTime(scheduleTime);
            }
            sendDetailSubList.add(sendDetailSub);
        });
        mtPack.setMsgs(messageDataList);
        log.info("短信请求业务号requestNo:{},批次号:{},参数组装完成,请求参数:{}", requestNo, batchId, JSON.toJSONString(mtPack));
        return mtPack;
    }


    /**
     *
     * @param bodyRequest
     * @param batchId
     * @return
     * @throws Exception
     */
    public MTPack getPack(SmsRequest<SmsExtend> bodyRequest, String batchId) throws Exception {
        MTPack pack = new MTPack();
        //批次号
        pack.setBatchID(UUID.fromString(batchId));
        //pack.setBatchID(requestMessage.getBatchId());
        //批次名为空,则取批次号字段
        pack.setBatchName(StringUtils.isNotBlank(bodyRequest.getBatchName()) ? bodyRequest.getBatchName() : batchId);
        //设置信息类型为短信
        pack.setMsgType(MTPack.MsgType.SMS);
        //定时发送时间，上游传的格式为yyyy-MM-dd HH:mm:ss,为空则传0
        pack.setScheduleTime(StringUtils.isNotBlank(bodyRequest.getScheduleTime()) ? (new SimpleDateFormat(Constants.TIME_FORMATTER).parse(bodyRequest.getScheduleTime()).getTime()) : 0);
        //短信过期发送时间，上游传的格式为yyyy-MM-dd HH:mm:ss,为空则传0
        pack.setDeadline(StringUtils.isNotBlank(bodyRequest.getDeadline()) ? (new SimpleDateFormat(Constants.TIME_FORMATTER).parse(bodyRequest.getDeadline()).getTime()) : 0);
        //设置业务类型编号(对应上游传的账号字段)
        pack.setBizType(Integer.parseInt(bodyRequest.getExtend().getAcctNo()));
        pack.setSendType(MTPack.SendType.GROUP);
        pack.setRemark(null);
        //pack.setMsgs(messageDataList);
        return pack;
    }

    @Override
    public void afterPropertiesSet()  {
        log.info("start-初始化: 玄武短信请求 PostMsg 和设置 gwHost");
        postMsg = new PostMsg(false, soTimeout, maxConns);
        //设置下发服务器的地址信息,gwHost设置(请求)
        postMsg.getCmHost().setHost(xuanwuProperties.getIp(), xuanwuProperties.getPort());
        log.info("end-初始化:玄武短信请求 PostMsg 和设置 gwHost");
    }










/*    @Override
    public JSONObject sendMsgs(SmsRequestMessage<JSONObject> requestMessage) {
        log.info("xw end sendSmsMs,请求报文：{}", JSON.toJSONString(requestMessage));
        String sendMode = requestMessage.getSendMode();

        Map<String, Object> bizTypeMap = (Map<String, Object>) xuanwuProperties.getBizTypeMap().get(requestMessage.getSmsMfgChanCd());
        log.info("获取通道信息为：{}", JSON.toJSONString(bizTypeMap));
        String batchId = requestMessage.getBatchId();
        String batchName = requestMessage.getBatchName();
        String schduleTimeStr = "";
        String deadlineStr = "";
        JSONObject resultJSONObject = new JSONObject();
        resultJSONObject.put("batchId",batchId);
        try {
            List<SmsRequestMessage.ParamInfo> paramInfoList = requestMessage.getSendDtl().stream().collect(Collectors.toList());
            List<MessageData> messageDataList = new ArrayList<>();
            if(CollectionUtils.isNotEmpty(paramInfoList)){
                for (int i = 0; i < paramInfoList.size(); i++) {
                    SmsRequestMessage.ParamInfo item = paramInfoList.get(i);
                    String custMsgId = item.getUuid();
                    String phoneNo = item.getCellPhoneNo();
                    String content = item.getContent();
                    if(StringUtils.equals(sendMode, SendModeEunm.DELAY.getCode())){
                        schduleTimeStr = requestMessage.getScheduleTime();
                        deadlineStr = requestMessage.getDeadline();
                    }
                    messageDataList.add(new MessageData(phoneNo, content, custMsgId));
                }
                log.info("messageDataList:{}",messageDataList.toString());
                resultJSONObject = senedGeneralMessage(getPack(batchId, batchName, (Integer) bizTypeMap.get(Constants.BIZTYPE), schduleTimeStr, deadlineStr, null, messageDataList), resultJSONObject);
            }else{
                resultJSONObject.put(Constants.RESULT, ResultEnum.EMPTY_DATALIST.getCode());
                resultJSONObject.put(Constants.MESSAGE, ResultEnum.EMPTY_DATALIST.getDescription());
            }
            log.info("xw end sendSmsMsg");
            return resultJSONObject;
        } catch (Exception e) {
            log.error("玄武短信发送失败，请求流水号:{},原因:{}", requestMessage.getRequestNo(), e.getMessage());
            resultJSONObject.put(Constants.RESULT, ResultEnum.EXP.getCode());
            resultJSONObject.put(Constants.MESSAGE,ResultEnum.EXP.getDescription());
            return resultJSONObject;
        }
    }




    public MTPack getPack(String batchId, String batchName, int bizType, String scheduleTimeStr, String deadlineStr,String remark, List<MessageData> messageDataList) throws Exception {
        MTPack pack = new MTPack();
        pack.setBatchID(UUID.fromString(batchId));//设置批次Id 如果不设置，SDK会自动生成  
        pack.setBatchName(StringUtils.isNotBlank(batchName)? batchName : batchId);//设置批次名称  
        pack.setMsgType(MTPack.MsgType.SMS);//设置信息类型为短信  
        pack.setScheduleTime(StringUtils.isNotBlank(scheduleTimeStr)?(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(scheduleTimeStr).getTime()):0);
        pack.setDeadline(StringUtils.isNotBlank(deadlineStr)?(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(deadlineStr).getTime()):0);
        pack.setBizType(bizType);//设置业务类型编号  
        pack.setSendType(MTPack.SendType.GROUP);//设置发送类型为群发  
        pack.setRemark(remark);
        pack.setMsgs(messageDataList);
        log.info("发送普通短信,pack:{}", pack);
        return pack;
    }

    public JSONObject senedGeneralMessage(MTPack pack, JSONObject resultJSONObject) throws Exception {
        PostMsg pm = new PostMsg(false, 30000, 1);
        pm.getCmHost().setHost(xuanwuProperties.getIp(), xuanwuProperties.getPort());//设置下发服务器的地址信息  
        GsmsResponse resp = pm.post(new Account(xuanwuProperties.getAccount(), xuanwuProperties.getPassword()), pack);
        log.info("玄武响应报文为:{}", resp);
        resultJSONObject.put("batchId",resp.getUuid().toString());
        resultJSONObject.put("result",resp.getResult());
        resultJSONObject.put("message",resp.getMessage());
        return resultJSONObject;
    }*/



}
